#include "QDC_V792.h"
#include "vme_sis.h"

// --- constructor ---
QDC_V792::QDC_V792(int handle,u_int32_t addr)
		: CAEN_module(handle,addr)
{
}


u_int32_t QDC_V792::GetNChannels()
{
	return N_CHANNELS;
}


u_int32_t QDC_V792::ReadEvent(EventStruct *ev)
{
		u_int32_t d;

		//clear event
		u_int16_t *adc_ptr = &(ev->adc00);

		for(int i=0; i<12; i++)	adc_ptr[i]=0xffff;
				
		//read header
		readA32D32(handle,address+OUTPUT_BUFFER,&d);

		//check if we got a header
		if( (d & IS_HEADER) != IS_HEADER )
				throw IOError(("QDC_V792::readEvent(): header expected, got 0x"+
											 hex(d)).c_str(),0);

		u_int16_t n_ch = (d & MEM_CHANNEL_MASK) >> MEM_CHANNEL_SHIFT;
    u_int32_t data[N_CHANNELS];

//		cerr << "QDC_V792::ReadEvent(), got header 0x" << hex << d
//				 << " -> reading data for " << dec << n_ch << " channels" << endl;

		readA32D32DMA(handle,address+OUTPUT_BUFFER,data,n_ch);

		for(int i=0; i<n_ch; i++)	{
				if( (data[i] & IS_INVALID_DATUM) != 0 )
						throw IOError(("QDC_V792::readEvent(): invalid datum, got "
													 +hex(data[i])).c_str(),0);

				u_int32_t ch    = (data[i] & CHANNEL_MASK) >> CHANNEL_SHIFT;
				u_int16_t value = (data[i] & VALUE_MASK) >> VALUE_SHIFT;

				if( (ch < 0) || (ch >11) ) 
						throw IOError((string("QDC_V792::readEvent(): got data for channel ")+ch+"("+hex(data[i])+")").c_str(),0);
				adc_ptr[ch] = value;
		}
		
		//read eob
		readA32D32(handle,address+OUTPUT_BUFFER,&d);

		//check if we got eob
		if( (d & IS_EOB) != IS_EOB ) 
				throw IOError(("QDC_V792::readEvent(): eob expected, got "+hex(d)).c_str(),0);

		//return the event counter, extracted from eob

		return (d & EOB_EVCOUNT_MASK);
}


u_int32_t QDC_V792::ReadEventFast(EventStruct *ev)
{
    u_int32_t data[PERKEO_CN+2];
				
		//read 14 words at once
		readA32D32DMA(handle,address+OUTPUT_BUFFER,data,PERKEO_CN+2);

		//check if we got a header
		if( (data[0] & IS_HEADER) != IS_HEADER )
				throw IOError(("QDC_V792::readEvent(): header expected, got 0x"+
											 hex(data[0])).c_str(),0);

		u_int32_t n_ch = (data[0] & MEM_CHANNEL_MASK) >> MEM_CHANNEL_SHIFT;
		if( n_ch != PERKEO_CN )
				throw IOError( ( string("QDC_V792::readEvent(): number of events extracted from header is ")
												 + n_ch + " - expected " + PERKEO_CN ).c_str(),0);
		
		//check if we got eob
		if( (data[PERKEO_CN+1] & IS_EOB) != IS_EOB ) 
				throw	IOError(("QDC_V792::readEvent(): eob expected, got "
											 +hex(data[PERKEO_CN+1])).c_str(),0);

		//clear event
		u_int16_t *adc_ptr = &(ev->adc00);
		for(int i=0; i<12; i++)	adc_ptr[i]=0xffff;

		for(int i=1; i<PERKEO_CN+1; i++)	{
				if( (data[i] & IS_INVALID_DATUM) != 0 )
						throw IOError(("QDC_V792::readEvent(): invalid datum, got "
													 +hex(data[i])).c_str(),0);

				u_int32_t ch    = (data[i] & CHANNEL_MASK) >> CHANNEL_SHIFT;
				u_int16_t value = (data[i] & VALUE_MASK) >> VALUE_SHIFT;

				if( (ch < 0) || (ch >11) ) 
						throw IOError((string("QDC_V792::readEvent(): got data for channel ")
													 +ch+"("+hex(data[i])+")").c_str(),0);
				adc_ptr[ch] = value;
		}
		
		//return the event counter, extracted from eob
		return (data[PERKEO_CN+1] & EOB_EVCOUNT_MASK);
}


u_int32_t QDC_V792::ReadEvents(EventStruct *ev,u_int32_t num)
{
		const static u_int32_t MAXEVENTS = 32;
    static u_int32_t data[MAXEVENTS*(PERKEO_CN+2)];


		if(num>MAXEVENTS)
				throw IOError( ( string("QDC_V792::readEvent(): "
																"number of events is ")
												 + num + " - maximum is " + MAXEVENTS ).c_str(),0);


		u_int32_t words = num*(PERKEO_CN+2);
				
		//read num*14 words at once
		readA32D32DMA(handle,address+OUTPUT_BUFFER,data,words);

		u_int32_t offset;
		u_int32_t j;
		offset = 0;
		for(j=0; j<num; j++) 	{
				offset = j*(PERKEO_CN+2);
				//check if we got a header
				if( (data[offset+0] & IS_HEADER) != IS_HEADER )
						throw IOError(("QDC_V792::readEvent(): header expected, got 0x"+
													 hex(data[offset+0])).c_str(),0);

				//check if the number of channels is correct
				u_int32_t n_ch =
						(data[offset+0] & MEM_CHANNEL_MASK) >> MEM_CHANNEL_SHIFT;
				if( n_ch != PERKEO_CN )
						throw IOError( ( string("QDC_V792::readEvent(): "
			  			"number of events extracted from header is ")
	   		  		 + n_ch + " - expected " + PERKEO_CN ).c_str(),0);
				
				//check if we got eob
				if( (data[offset+PERKEO_CN+1] & IS_EOB) != IS_EOB ) 
						throw	IOError(("QDC_V792::readEvent(): eob expected, got "
													 +hex(data[offset+PERKEO_CN+1])).c_str(),0);
				
				//clear event
				u_int16_t *adc_ptr = &(ev[j].adc00);
				for(int i=0; i<12; i++)	adc_ptr[i]=0xffff;
				
				for(u_int32_t i=1; i<n_ch+1; i++)	{
						if( (data[offset+i] & IS_INVALID_DATUM) != 0 )
								throw IOError(("QDC_V792::readEvent(): invalid datum, got "
															 +hex(data[offset+i])).c_str(),0);

						u_int32_t ch    = (data[offset+i] & CHANNEL_MASK) >> CHANNEL_SHIFT;
						u_int16_t value = (data[offset+i] & VALUE_MASK) >> VALUE_SHIFT;

						if( (ch < 0) || (ch >11) ) 
						throw IOError((string("QDC_V792::readEvent(): "
																	"got data for channel ")
													 +ch+"("+hex(data[offset+i])+")").c_str(),0);
						adc_ptr[ch] = value;
				}
		}
		
		//return the event counter of the last event, extracted from its eob
    return (data[offset+PERKEO_CN+1] & EOB_EVCOUNT_MASK);
}
